/**
 * Copyright 2006 Brad Reynolds
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.leeloo.databinding.internal.ui;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.leeloo.core.Tag;

/**
 * Dialog that allows the user to add, remove, and select {@link Tag Tags}.
 * <p>
 * API with a category of "testability" is only visible for testing purposes.
 * </p>
 * 
 * @author Brad Reynolds
 */
public class TagSelectionDialog extends Dialog {
    /**
     * Tags to display.
     */
    private List tags;

    /**
     * Tags that are selected.
     */
    private List selection;

    /**
     * Represents no strings.
     */
    private static final Tag[] NO_TAGS = new Tag[0];

    /**
     * Viewer that the user views and selects filters to apply.
     */
    private CheckboxTableViewer viewer;    
    
    /**
     * Constructs a new instance.
     * 
     * @param parentShell
     * @param tags
     *            tags to display, can be <code>null</code>
     * @param selection
     *            tags to be selected, can be <code>null</code>
     */
    public TagSelectionDialog(Shell parentShell, Tag[] tags, Tag[] selection) {
        super(parentShell);

        this.tags = new ArrayList();
        if (tags != null) {
            this.tags.addAll(Arrays.asList(tags));
        }

        this.selection = new ArrayList();
        if (selection != null) {
            this.selection.addAll(Arrays.asList(selection));
        }
    }

    /**
     * Constructs the UI for the dialog.
     *  
     * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
     */
    protected Control createDialogArea(Composite parent) {
        Composite composite = new Composite((Composite) super.createDialogArea(parent), SWT.NONE);
        composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        composite.setLayout(new GridLayout(2, false));

        viewer = CheckboxTableViewer.newCheckList(composite, SWT.NONE);
        viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        viewer.setContentProvider(new ArrayContentProvider());
        viewer.setInput(tags);
        
        if (selection != null) {
            viewer.setCheckedElements(selection.toArray(new Tag[selection.size()]));
        }
        viewer.addCheckStateListener(new ICheckStateListener() {
            public void checkStateChanged(CheckStateChangedEvent event) {
                viewer.setChecked(event.getElement(), event.getChecked());
                
                if (event.getChecked()) {
                    selection.add(event.getElement());
                } else {
                    selection.remove(event.getElement());
                }
            }
        });

        createButtons(composite);
        
        return composite;
    }
    
    /**
     * Creates the New and Remove buttons.
     * 
     * @param parent
     */
    private void createButtons(Composite parent) {
        Composite buttons = new Composite(parent, SWT.NONE);
        buttons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        buttons.setLayout(new GridLayout());
        Button newButton = new Button(buttons, SWT.PUSH);
        newButton.setText(Messages.getString("TagSelectionDialog.new")); //$NON-NLS-1$
        newButton.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                InputDialog dialog = new InputDialog(viewer.getControl().getShell(),
                        Messages.getString("TagSelectionDialog.title"), //$NON-NLS-1$
                        Messages.getString("TagSelectionDialog.message"), //$NON-NLS-1$
                        null,
                        new TagFilterValidator());
                if (dialog.open() == InputDialog.OK) {
                    add(Tag.fromDisplay(dialog.getValue()));
                }
            }
        });
        
        GridDataFactory factory = GridDataFactory.fillDefaults().align(SWT.FILL, SWT.NONE);
        factory.applyTo(newButton);
        
        final Button removeButton = new Button(buttons, SWT.PUSH);
        removeButton.setText(Messages.getString("TagSelectionDialog.remove")); //$NON-NLS-1$
        removeButton.setEnabled(false);
        removeButton.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                StructuredSelection selection = (StructuredSelection) viewer.getSelection();
                remove((Tag) selection.getFirstElement());
            }
        });
        
        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                StructuredSelection selection = (StructuredSelection) event.getSelection();
                removeButton.setEnabled(!selection.isEmpty());
            }            
        });
        
        factory.applyTo(newButton);
    }
    
    /**
     * Sets the initial size of the Shell.
     * 
     * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
     */
    protected void configureShell(Shell newShell) {
        super.configureShell(newShell);
        
        newShell.setSize(400, 300);
    }
    
    /**
     * Returns the tags that are selected in the dialog.
     * 
     * @return selected tags, empty array if there is no selection.  Will not be <code>null</code>.
     */
    public Tag[] getSelection() {
        return (Tag[]) selection.toArray(new Tag[selection.size()]);
    }

    /**
     * Returns the tags that are displayed in the dialog.
     * 
     * @return all tags, empty array if there are none.  Will not be <code>null</code>.
     */
    public Tag[] getTags() {
        return (Tag[]) ((tags.size() > 0) ? tags.toArray(new Tag[tags.size()]) : NO_TAGS);
    }

    /**
     * Adds an item to the dialog.
     * 
     * @category testability
     * @param tag
     */
    public void add(Tag tag) {
        tags.add(tag);
        viewer.add(tag);
        viewer.setChecked(tag, true);
        selection.add(tag);
    }

    /**
     * Removes an item from the dialog.
     * 
     * @category testability
     * @param tag
     */
    public void remove(Tag tag) {
        tags.remove(tag);
        selection.remove(tag);
        viewer.remove(tag);
    }
}
